Skip to content

Architecture Diagram

# Architecture Diagram

Phase 1 — ExzenTCG Homelab


Network Topology

flowchart TB
    subgraph Internet
        USER["User Browser"]
        CF["Cloudflare Edge<br/>TLS termination"]
        CFZT["Cloudflare Zero Trust<br/>Access Policy"]
        WEBHOOK["External Webhooks<br/>(Stripe, Telegram, etc.)"]
    end

    subgraph Proxmox["Proxmox VE (192.168.0.200)"]
        subgraph CT101["CT 101 — edge-gateway (192.168.0.51)"]
            CFLRD["cloudflared<br/>:7844 → CF edge"]
            NPM["Nginx Proxy Manager<br/>:80 (localhost only)<br/>:81 (admin, LAN)"]
        end

        subgraph CT102["CT 102 — n8n-app (192.168.0.52)"]
            N8N["n8n v1.80.0<br/>:5678 (LAN only)"]
        end

        subgraph CT103["CT 103 — homepage (192.168.0.53)"]
            DASH["Homepage Dashboard<br/>:3000"]
        end

        AMT["Intel AMT<br/>:16992"]
        PVFW["Proxmox Firewall<br/>Per-CT rules + IP sets"]
    end

    subgraph Admin["Admin Access"]
        DESKTOP["Admin Desktop<br/>192.168.0.16"]
        LAPTOP["Admin Laptop<br/>via Tailscale"]
        TS["Tailscale Mesh<br/>100.89.70.63<br/>Subnet: 192.168.0.0/24"]
    end

    ROUTER["Home Router (D-Link)<br/>192.168.0.1<br/>DNS / DHCP / NAT"]

    %% Traffic flow
    USER -->|"HTTPS :443"| CF
    WEBHOOK -->|"HTTPS :443"| CF
    CF -->|"Access check"| CFZT
    CFZT -->|"Tunnel (TCP :7844)"| CFLRD
    CFLRD -->|"HTTP localhost:80"| NPM
    NPM -->|"HTTP :5678"| N8N
    NPM -->|"HTTP :3000"| DASH

    %% Homepage widget connections
    DASH -.->|"API :8006"| Proxmox
    DASH -.->|"API :81"| NPM
    DASH -.->|"API :5678"| N8N

    %% Admin access
    DESKTOP -->|":81 NPM admin"| NPM
    DESKTOP -->|":22 SSH"| N8N
    DESKTOP -->|":8006 WebUI"| Proxmox
    LAPTOP -->|"WireGuard"| TS
    TS -->|":8006 WebUI"| Proxmox
    TS -->|"Subnet route"| ROUTER
    TS -->|"Subnet route"| AMT

    %% DNS
    CT101 -->|"UDP :53"| ROUTER
    CT102 -->|"UDP :53"| ROUTER
    CT103 -->|"UDP :53"| ROUTER

    %% Firewall
    PVFW -.->|"enforces"| CT101
    PVFW -.->|"enforces"| CT102
    PVFW -.->|"enforces"| CT103

    style CF fill:#f38020,color:#fff
    style CFZT fill:#f38020,color:#fff
    style CT101 fill:#1a3a4a,color:#fff
    style CT102 fill:#1a3a4a,color:#fff
    style CT103 fill:#1a3a4a,color:#fff
    style N8N fill:#ea4b71,color:#fff
    style DASH fill:#059669,color:#fff
    style TS fill:#4c8bf5,color:#fff
    style AMT fill:#555,color:#fff

Traffic Flow (request lifecycle)

n8n.exzentcg.com / dash.exzentcg.com:
  1. User visits https://n8n.exzentcg.com (or dash.exzentcg.com)
  2. DNS resolves → CNAME → cfargotunnel.com → Cloudflare edge IP
  3. Cloudflare terminates TLS
  4. Cloudflare Zero Trust checks Access policy
     → /webhook/* paths: bypass (no auth)
     → all other paths: require email login (Google OAuth or one-time PIN)
  5. Authenticated request enters Cloudflare Tunnel
  6. cloudflared (CT 101, network_mode: host) receives on TCP 7844
  7. cloudflared forwards to http://localhost:80 (NPM)
  8. NPM matches Host header → routes to correct backend:
     → n8n.exzentcg.com  → http://192.168.0.52:5678 (n8n)
     → dash.exzentcg.com → http://192.168.0.53:3000 (Homepage)
  9. Backend processes request and responds
  10. Response travels back through the same chain

Remote admin (Tailscale):
  1. Laptop connects to Tailnet (WireGuard)
  2. Tailscale subnet route (192.168.0.0/24) advertised by Proxmox host
  3. All LAN devices reachable: Proxmox :8006, Router :80, AMT :16992, NPM :81
  4. Tailscale traffic bypasses Proxmox firewall (ts-input chain, see D3)

Firewall Boundaries

                    ┌─────────────────────────────┐
                    │      INTERNET (any)          │
                    └─────────┬───────────────────-┘
                              │
                    ┌─────────▼───────────────────-┐
                    │    Cloudflare Edge + Tunnel   │
                    │    (TLS + Access + WAF)       │
                    └─────────┬───────────────────-┘
                              │ TCP 7844
              ┌───────────────▼───────────────────-┐
              │  CT 101 — edge-gateway             │
              │                                    │
              │  IN:  localhost:80,443 only         │
              │       +admin_desktop:81             │
              │       CT 103:81 (widget)           │
              │       everything else: DROP         │
              │                                    │
              │  OUT: router:53 (DNS)              │
              │       .52:5678 (n8n)               │
              │       .53:3000 (homepage)          │
              │       DROP +lan_subnet ◄── key!    │
              │       7844 (tunnel)                │
              │       443, 80 (APIs)               │
              └──────┬────────────┬───────────────-┘
                     │ TCP 5678   │ TCP 3000
              ┌──────▼──────┐  ┌──▼──────────────-─┐
              │ CT 102      │  │ CT 103             │
              │ n8n-app     │  │ homepage           │
              │             │  │                    │
              │ IN:         │  │ IN:                │
              │  +edge:5678 │  │  +edge:3000        │
              │  +admin:22  │  │  +admin:22,3000    │
              │  CT103:5678 │  │  DROP              │
              │  +admin:5678│  │                    │
              │  DROP       │  │ OUT:               │
              │             │  │  .200:8006 (PVE)   │
              │ OUT:        │  │  .51:81 (NPM)      │
              │  router:53  │  │  .52:5678 (n8n)    │
              │  DROP +lan  │  │  DROP +lan ◄─ key! │
              │  443 only   │  │  443 (HTTPS)       │
              └─────────────┘  └───────────────────-┘

Important

The DROP +lan_subnet rule before ACCEPT :443 is the critical lateral movement prevention. It ensures that even if a container is compromised, it cannot reach other LAN devices (desktop, router admin, other containers) — even on port 443.


IP Address Map

IP Role Access
192.168.0.1 Home router DNS/DHCP/NAT
192.168.0.16 Admin desktop In admin_desktop IP set
192.168.0.51 edge-gateway (CT 101) NPM + cloudflared
192.168.0.52 n8n-app (CT 102) n8n only
192.168.0.53 homepage (CT 103) Homepage dashboard
192.168.0.54 ollama (CT 104) Ollama + Open WebUI
192.168.0.55 tcg-staging (CT 105) Medusa staging backend
192.168.0.56 exzen-staging (CT 106) exzen-core FastAPI staging (Telegram bots in long-poll mode)
192.168.0.57 dev-shell (CT 107) Node 22 + Docker dev environment; Claude Code installed
192.168.0.58 shopee-print (CT 108) shopee-auto-print service; USB passthrough to Fujun 9250 thermal printer
192.168.0.200 Proxmox host Hypervisor
192.168.0.200:16992 Intel AMT Out-of-band management (MeshCommander)
100.89.70.63 Proxmox via Tailscale Remote admin + subnet route (192.168.0.0/24)
100.121.171.73 telebot-prod via Tailscale Oracle Cloud production exzen-core (Tailscale SSH enabled)